package com.salmon.oss.service;

import com.google.common.base.Splitter;
import com.salmon.oss.common.api.ApiResponse;
import com.salmon.oss.core.usermgr.UserService;
import com.salmon.oss.core.usermgr.model.SystemRole;
import com.salmon.oss.core.usermgr.model.UserInfo;
import com.salmon.oss.security.OperationAccessControl;
import com.salmon.oss.server.BucketService;
import com.salmon.oss.server.store.OssStoreService;
import com.salmon.thrift.InvalidOperation;
import com.salmon.thrift.OssHandlerService;
import com.salmon.thrift.domain.BucketInfo;
import com.salmon.thrift.domain.ObjectListResult;
import com.salmon.thrift.domain.ObjectMetaData;
import com.salmon.thrift.domain.OssObject;
import com.salmon.thrift.domain.OssObjectSummary;
import org.apache.commons.io.IOUtils;
import org.apache.thrift.TException;
import org.springframework.beans.BeanUtils;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class OssHandlerServiceImpl implements OssHandlerService.Iface {


    private BucketService bucketService;

    private OssStoreService ossStoreService;

    private OperationAccessControl operationAccessControl;

    private UserService userService;

    public void setBucketService(BucketService bucketService) {
        this.bucketService = bucketService;
    }

    public void setOssStoreService(OssStoreService ossStoreService) {
        this.ossStoreService = ossStoreService;
    }

    public void setOperationAccessControl(OperationAccessControl operationAccessControl) {
        this.operationAccessControl = operationAccessControl;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public boolean createBucket(String bucketName, String detail, String token) throws InvalidOperation, TException {
        UserInfo currentUser = userService.getUserInfo(token);
        //判断当前用户是否有权限创建 BUCKET
        if (!currentUser.getSystemRole().equals(SystemRole.VISITER)) {
            bucketService.addBucket(currentUser, bucketName, detail);
            try {
                ossStoreService.createBucketStore(bucketName);
            } catch (IOException ioe) {
                bucketService.deleteBucket(bucketName);
                throw new InvalidOperation(500, ioe.getMessage());
            }
            return true;
        }
        throw new InvalidOperation(401,"Permission denied");
    }

    @Override
    public boolean deleteBucket(String bucket, String token) throws InvalidOperation, TException {
        UserInfo currentUser = userService.getUserInfo(token);
        // 判断当前用户是否有权对 bucket 进行删除
        if (operationAccessControl.checkBucketOwner(currentUser.getUserName(), bucket)) {
            try {
                ossStoreService.deleteBucketStore(bucket);
            } catch (IOException ioe) {
                throw new InvalidOperation(50003, "delete bucket error, bucket [" + bucket +"] is exist?");
            }
            bucketService.deleteBucket(bucket);
            return true;
        }
        throw new InvalidOperation(401,"Permission denied");
    }

    @Override
    public BucketInfo getBucketInfo(String bucket, String token) throws InvalidOperation, TException {
        com.salmon.oss.common.domain.BucketInfo bucketInfo = bucketService.getBucketByName(bucket);
        if (operationAccessControl.checkPermission(token, bucketInfo.getBucketName())) {
            BucketInfo target = new BucketInfo();
            BeanUtils.copyProperties(bucketInfo, target, "createTime");
            target.setCreateTime(bucketInfo.getCreateTime().getTime());
            return target;
        }
        throw new InvalidOperation(401,"Permission denied");
    }

    @Override
    public List<BucketInfo> listBucket(String token) throws InvalidOperation, TException {
        List<com.salmon.oss.common.domain.BucketInfo> sourceList = bucketService.getUserBuckets(token);
        List<BucketInfo> targetList = new ArrayList<>();
        sourceList.forEach(source -> {
            BucketInfo target = new BucketInfo();
            BeanUtils.copyProperties(source, target, "createTime");
            target.setCreateTime(source.getCreateTime().getTime());
            targetList.add(target);
        });
        return targetList;
    }


    /**
     * 根据前缀过滤
     *
     * @param bucket
     * @param dir
     * @param prefix
     * @param start
     * @param token
     */
    @Override
    public ObjectListResult listObjectByPrefix(String bucket, String dir, String prefix, String start, String token) throws InvalidOperation, TException {
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401,"Permission denied");
        }
        if (!dir.startsWith("/") || !dir.endsWith("/")) {
            throw new InvalidOperation(400,"dir must start with / and end with /");
        }
        if ("".equals(start) || start.equals("/")) {
            start = null;
        }
        if (start != null) {
            List<String> segs = StreamSupport.stream(Splitter
                    .on("/")
                    .trimResults()
                    .omitEmptyStrings().split(start).spliterator(), false).collect(Collectors.toList());
            start = segs.get(segs.size() - 1);
        }
        try {
            com.salmon.oss.common.domain.ObjectListResult result = this.ossStoreService.listByPrefix(bucket, dir, prefix, start, 100);
            ObjectListResult listResult = new ObjectListResult();
            BeanUtils.copyProperties(result, listResult, "objectList");
            List<com.salmon.oss.common.domain.OssObjectSummary> summaryList = result.getObjectList();
            List<OssObjectSummary> targetList = new ArrayList<>();
            summaryList.forEach(summary -> {
                OssObjectSummary objectSummary = new OssObjectSummary();
                BeanUtils.copyProperties(summary, objectSummary);
                targetList.add(objectSummary);
            });
            listResult.setObjectList(targetList);
            return listResult;
        } catch (IOException e) {
           throw new InvalidOperation(500, e.getMessage());
        }
    }

    /**
     * 列出bucket对应目录下的所有文件，当start key 不为空时，从start key进行查询
     *
     * @param bucket
     * @param dir
     * @param start
     * @param token
     */
    @Override
    public ObjectListResult listObjectByDir(String bucket, String dir, String start, String token) throws InvalidOperation, TException {
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401,"Permission denied");
        }
        if (!dir.startsWith("/") || !dir.endsWith("/")) {
            throw new InvalidOperation(400,"dir must start with / and end with /");
        }
        if ("".equals(start) || start.equals("/")) {
            start = null;
        }
        if (start != null) {
            List<String> segs = StreamSupport.stream(Splitter
                    .on("/")
                    .trimResults()
                    .omitEmptyStrings().split(start).spliterator(), false).collect(Collectors.toList());
            start = segs.get(segs.size() - 1);
        }

        try {
            com.salmon.oss.common.domain.ObjectListResult result = this.ossStoreService.listDir(bucket, dir, start, 100);
            ObjectListResult listResult = new ObjectListResult();
            BeanUtils.copyProperties(result, listResult, "objectList");
            List<com.salmon.oss.common.domain.OssObjectSummary> summaryList = result.getObjectList();
            List<OssObjectSummary> targetList = new ArrayList<>();
            summaryList.forEach(summary -> {
                OssObjectSummary objectSummary = new OssObjectSummary();
                BeanUtils.copyProperties(summary, objectSummary);
                targetList.add(objectSummary);
            });
            listResult.setObjectList(targetList);
            return listResult;
        } catch (IOException e) {
           throw new InvalidOperation(500, e.getMessage());
        }
    }

    /**
     * 获取文件详情
     * @param bucket
     * @param key
     * @param token
     * @return
     * @throws TException
     */
    @Override
    public OssObjectSummary getObjectSummary(String bucket, String key, String token) throws InvalidOperation, TException {
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401,"Permission denied");
        }
        try {
            com.salmon.oss.common.domain.OssObjectSummary summary = ossStoreService.getSummary(bucket, key);
            if (summary == null) {
                throw new TException("not found");
            }
            OssObjectSummary objectSummary = new OssObjectSummary();
            BeanUtils.copyProperties(summary, objectSummary);
            return objectSummary;
        } catch (IOException e) {
            throw new InvalidOperation(500, e.getMessage());
        }
    }

    /**
     * 安起止文件名进行检索
     * @param bucket
     * @param startKey
     * @param endKey
     * @param token
     * @return
     * @throws TException
     */
    @Override
    public ObjectListResult listObject(String bucket, String startKey, String endKey, String token) throws InvalidOperation, TException {
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401,"Permission denied");
        }
        if (startKey.compareTo(endKey) > 0) {
            throw new InvalidOperation(400,"endKey must be bigger than startKey");
        }
        try {
            com.salmon.oss.common.domain.ObjectListResult result = new com.salmon.oss.common.domain.ObjectListResult();
            List<com.salmon.oss.common.domain.OssObjectSummary> summaryList = ossStoreService.list(bucket, startKey, endKey);
            ObjectListResult objectListResult = new ObjectListResult();
            BeanUtils.copyProperties(result, objectListResult, "bucket", "maxKey", "minKey", "objectCount", "objectList");
            objectListResult.setBucket(bucket);
            if (summaryList.size() > 0) {
                objectListResult.setMaxKey(summaryList.get(summaryList.size() - 1).getKey());
                objectListResult.setMinKey(summaryList.get(0).getKey());
            }
            objectListResult.setObjectCount(summaryList.size());
            List<OssObjectSummary> targetList = new ArrayList<>();
            summaryList.forEach(summary -> {
                OssObjectSummary objectSummary = new OssObjectSummary();
                BeanUtils.copyProperties(summary, objectSummary);
                targetList.add(objectSummary);
            });
            objectListResult.setObjectList(targetList);
            return objectListResult;
        } catch (IOException e) {
            throw new InvalidOperation(500, e.getMessage());
        }
    }

    /**
     * 下载文件
     *
     * @param bucket bucket 名称
     * @param key    文件全路径
     * @return
     * @throws TException
     */
    @Override
    public OssObject getObject(String bucket, String key, String token) throws InvalidOperation, TException {
        //校验权限
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401,"Permission denied");
        }
        try {
            com.salmon.oss.common.domain.OssObject object = this.ossStoreService.getObject(bucket, key);
            if (object == null) { // 如果未能查询到要下载的文件
                throw new InvalidOperation(404,"file [" + key + "] is not found");
            }
            com.salmon.oss.common.domain.ObjectMetaData source = object.getMetaData();
            ObjectMetaData targetMetaData = new ObjectMetaData();
            BeanUtils.copyProperties(source, targetMetaData);
            byte[] content = IOUtils.toByteArray(object.getContent());
            OssObject ossObject = new OssObject();
            ossObject.setContent(content);
            ossObject.setMetaData(targetMetaData);
            return ossObject;
        } catch (IOException e) {
            throw new InvalidOperation(500, e.getMessage());
        }
    }

    @Override
    public boolean deleteObject(String bucket, String key, String token) throws InvalidOperation, TException {
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401, "Permission denied");
        }
        try {
            this.ossStoreService.deleteObject(bucket, key);
            return true;
        } catch (Exception e) {
           throw new InvalidOperation(500, e.getMessage());
        }
    }

    /**
     * 上传文件
     */
    @Override
    public boolean putObject(String bucket, String key, ByteBuffer buffer, String mediaType, Map<String, String> attrs, String token) throws InvalidOperation, TException {
        // 检验某个TOKEN是否具有对bucket操作的权限, 用户的userId 和token值在创建时保持一致
        if (!operationAccessControl.checkPermission(token, bucket)) {
            throw new InvalidOperation(401, "Permission denied");
        }
        if (!key.startsWith("/")) {
            throw new InvalidOperation(400, "object key must start with /");
        }
        try {
            ossStoreService.put(bucket, key, buffer, buffer.array().length, mediaType, attrs);
            return true;
        } catch (Exception e) {
           throw new InvalidOperation(500,e.getMessage());
        } finally {
            if(buffer != null)
                buffer.clear();
        }
    }
}
